// ======================================================================
// Copyright (C) 2013 Hell-Prototypes. / www.hellprototypes.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// ======================================================================
#include "hell_arm.h"
#include "ui_hell_arm.h"
#ifdef __WIN_PORT__
#include <WTypes.h>
#include <dbt.h>
#include <Windows.h>
#endif

#define CMD_TYPE_BOOT_2ND_APP       0x3F
#define USB_BULK_EP_IN          0x86

bool pap_thread::usb_connect_ctrl()
{
    if(m_usb_dev == NULL) {
        usb_init(); /* initialize the library */
        usb_find_busses(); /* find all busses */
        usb_find_devices(); /* find all connected devices */

        if (!(m_usb_dev = open_dev())) {
            emit disp_system_msg("Can't opening USB device: " + (QString)usb_strerror());
            return false;
        } else {
            //qDebug("open_dev success: device %04X:%04X opened", MY_VID, MY_PID);
            if (usb_claim_interface(m_usb_dev, 0) < 0) {
                emit disp_system_msg("error claiming interface :" + (QString)usb_strerror());
                usb_close(m_usb_dev);
                m_usb_dev = NULL;
                return false;
            } else {
                //qDebug("success: claim_interface");
            }
            usb_boot_2nd_app();
            emit disp_system_msg("USB open OK");
            emit usb_connected();

            //stop_arm_driver();
            return true;
        }
    } else {
        //stop_arm_driver();
        usb_close(m_usb_dev);
        m_usb_dev = NULL;
    }
    return false;
}

/* open usb device */
usb_dev_handle* pap_thread::open_dev(void)
{
    struct usb_bus *bus;
    struct usb_device *dev;

    for (bus = usb_get_busses(); bus; bus = bus->next) {
        for (dev = bus->devices; dev; dev = dev->next) {
            if (dev->descriptor.idVendor == MY_VID
                    && dev->descriptor.idProduct == MY_PID) {
                return usb_open(dev);
            }
        }
    }
    return NULL;
}

/* usb send control command */
bool pap_thread::send_ctrl_cmd(char *cmd_data, int cmd_len)
{
    if(m_usb_dev == NULL) {
        return false;
    }
    if( usb_control_msg(m_usb_dev, 0x40, 0xb0, 0x00ba, 0x0000,
                        cmd_data, cmd_len, 100) < 0) {
        return false;
    }
    return true;
}

void pap_thread::usb_boot_2nd_app()
{
    //This command was send to S3AN_IAP FW
    char cmd = CMD_TYPE_BOOT_2ND_APP;
    //qDebug("Send boot command");
    send_ctrl_cmd(&cmd, 1);
}

int pap_thread::usb_Ctrl_CMD(const char command, const uint param)
{
    char cmd[5];
    char retry = 3;

    while(retry--) {
        //sleep(100);
        cmd[0] = command;
        cmd[1] = (char)((param>>24) & 0xFF);
        cmd[2] = (char)((param>>16) & 0xFF);
        cmd[3] = (char)((param>>8)  & 0xFF);
        cmd[4] = (char)(param       & 0xFF);
        if(send_ctrl_cmd(cmd, (command & 0x80) ? 5 : 1) ){
            return 0;
        }
    }
    return -1;
}

/* usb read bulk data from bulk endpoint */
int pap_thread::read_bulk_data(char *rd_buffer, int len)
{
    int timeout;

    if(m_usb_dev == NULL) {
        return -1;
    }

    if(len > 4) {
        timeout = 1000;
    } else {
        timeout = 100;
    }
    return usb_bulk_read(m_usb_dev, USB_BULK_EP_IN, rd_buffer, len, timeout);
}

void pap_thread::usb_auto_connect_ctrl()
{
    usb_find_busses(); /* find all busses */
    usb_find_devices(); /* find all connected devices */

    struct usb_bus *bus;
    struct usb_device *dev;
    bool dev_online = false;
    bool break_outer_loop = false;

    for (bus = usb_get_busses(); bus; bus = bus->next) {
        for (dev = bus->devices; dev; dev = dev->next) {
            if (dev->descriptor.idVendor == MY_VID
                    && dev->descriptor.idProduct == MY_PID) {
                dev_online = true;
                break_outer_loop = true;
                break;
            }
        }
        if(break_outer_loop) {
            break;
        }
    }
    if(dev_online) {
        if (m_usb_dev == NULL) {
           usb_connect_ctrl();
        }
    } else {
        if (m_usb_dev != NULL) {
            usb_connect_ctrl();
        }
    }
}

bool Hell_Arm::nativeEvent(const QByteArray & eventType, void * message, long * result)
{
    Q_UNUSED(eventType);
    Q_UNUSED(result);

    if(((MSG *)message)->message == WM_DEVICECHANGE) {
        if (((MSG *)message)->wParam == DBT_DEVNODES_CHANGED) {
            m_pap_thread->usb_auto_connect_ctrl();

            if(m_pap_thread->m_usb_dev == NULL) {
                ui->pb_connect_device->setText("Connect...");
            } else {
                ui->pb_connect_device->setText("Disconnect");
            }
        }
    }

    return false;
}

